
//
// 130310 - AH - Created.
// 130316 - AH - Main program to create protocol XML from created protocol by
//               scanning annotations.
//

package org.japril.servlet.httpannotations;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.StringTokenizer;

import org.japril.jutil.CommandLineParser;
import org.japril.jutil.IOUtil;
import org.japril.servlet.HttpProtocolDef;
import org.japril.servlet.HttpRequestDef;
import org.japril.servlet.HttpRequestParamDef;

/**
 * <p>
 *   Creates 
 *   {@link HttpProtocolDef} by scanning annotations defined in this package.
 * </p>
 * @author Arne Halvorsen (AH)
 */
public final class HttpProtocolFactory 
{
  /**
   * <p>
   *   Constructor.
   * </p>
   */
  public HttpProtocolFactory(){}
  
  /**
   * <p>
   *   Scan given 
   *   {@link Class} for annotations defined in package
   *   {@code org.japril.servlet.httpannotations} to create 
   *   {@link HttpRequestDef} added to a created
   *   {@link HttpProtocolDef}.
   * </p>
   * @param clazz {@code Class} to scan.
   * @return {@code HttpProtocolDef} created.
   */
  public HttpProtocolDef create(Class<?> clazz){ return create(clazz, null); }
  
  /**
   * <p>
   *   Scan given 
   *   {@link Class} for annotations defined in package
   *   {@code org.japril.servlet.httpannotations} to create 
   *   {@link HttpRequestDef} added to a
   *   {@link HttpProtocolDef}.
   * </p>
   * @param clazz {@code Class} to scan.
   * @param pdef  {@code HttpProtocolDef} to add to, if {@code null} creates.
   * @return {@code HttpProtocolDef} added created
   *         {@code HttpRequestDef} to.
   */
  public HttpProtocolDef create(Class<?> clazz, HttpProtocolDef pdef)
  {
    if (clazz==null)
    {
      throw new NullPointerException("clazz");
    }
    
    HttpProtocol protocol=clazz.getAnnotation(HttpProtocol.class);
    if (protocol!=null)
    { 
      pdef=(pdef==null) ? new HttpProtocolDef(protocol.value()) : pdef;
      
      Method[] methods=clazz.getMethods();
      for (Method method : methods)
      {
        HttpRequest req=method.getAnnotation(HttpRequest.class);
        if (req!=null)
        {
          String action=getReqAction(req.action(), method);
          
          boolean post=req.post();
          
          HttpRequestDef reqDef=new HttpRequestDef(post, action);
          
          String desc=req.desc().trim();
          if (desc.length()>0) reqDef.description(desc);
          
          reqDef.authentication(req.authentication());
          reqDef.name(req.name());
         
          String enctype=req.enctype().trim();
          if (enctype.length()>0) reqDef.enctype(enctype);
          
          pdef.addHttpRequestDef(reqDef);
          
          Class<?>[] pars=method.getParameterTypes();
          Annotation[][] pans=method.getParameterAnnotations();
          int n=pars.length;
          for (int i=0; i<n; i++)
          {
            Class<?> type=pars[i];
            HttpRequestParam param=getHttpRequestParam(pans[i]);
            
            String name=(param==null) ? toHttpName(type.getSimpleName()) :
                                        param.name();
            
            boolean mandatory=(param==null) ? false : param.mandatory();
            
            String ftype=(param==null) ? "text" : param.ftype();
            
            String fvalue=(param==null) ? "" : param.fvalue();
            
            String options=(param==null) ? "" : param.options();
            
            String pdesc=(param==null) ? "" : param.desc();
            
            String info=(param==null) ? "" : param.info();
            
            int size=(param==null) ? -1 : param.size();
            
            int rows=(param==null) ? -1 : param.rows();
            
            int cols=(param==null) ? -1 : param.columns();
            
            HttpRequestParamDef pardef=new HttpRequestParamDef(name);
            pardef.type(type);
            pardef.mandatory(mandatory);
            pardef.ftype(ftype);
            if (fvalue.length()>0) pardef.fvalue(fvalue);
            setOptions(pardef, options);
            if (pdesc.length()>0) pardef.desc(pdesc);
            if (info.length()>0) pardef.info(pdesc);
            if (size>0) pardef.size(size);            
            if (rows>0) pardef.rows(rows);
            if (cols>0) pardef.cols(cols);
            
            reqDef.parameter(pardef);
          }
        }
      }
    }
    
    return pdef;
  }
  
  private static void setOptions(HttpRequestParamDef pardef, String v)
  {
    v=(v==null) ? "" : v.trim();
    StringTokenizer st=new StringTokenizer(v, ",");
    while (st.hasMoreTokens())
    {
      String option=st.nextToken().trim();
      if (option.length()>0) pardef.option(option);
    }
  }
  
  private static HttpRequestParam getHttpRequestParam(Annotation[] anns)
  {
    for (Annotation a : anns)
    {
      if (a instanceof HttpRequestParam) return (HttpRequestParam)a;  
    }
    return null;
  }
  
  private static String getReqAction(String name, Method method)
  {
    return name.equals("[from-method-name]") ? toHttpName(method.getName()) : 
                                               name;
  }
  
  private static String toHttpName(String name)
  {
    StringBuilder sb=new StringBuilder();
    int n=name.length();
    for (int i=0; i<n; i++)
    {
      char c=name.charAt(i);
      if (Character.isUpperCase(c))
      {
        if (i>0) sb.append("-");
        sb.append(Character.toLowerCase(c));
      }
      else
      {
        sb.append(c);
      }
    }
    return sb.toString();
  }
  
  /**
   * <p>
   *   Program that creates a protocol definition from an annotated class or
   *   read protocol definition from XML.
   * </p>
   * <table border='1'>
   *   <tr><th>Option</th><th>Desc</th><th>Mandatory</th></tr>
   *   <tr>
   *     <td>{@code -class <qname>}</td>
   *     <td>
   *       Qualified name on class to create protocol definition from.
   *     </td>
   *     <td>Yes unless {@code -in} option given</td>
   *   </tr>
   *   <tr>
   *     <td>{@code -in <path>}</td>
   *     <td>
   *       Path to XML file to parse. Ignored if {@code -class} option is 
   *       given.
   *     </td>
   *     <td>
   *       Is mandatory if {@code -class} option is not given.
   *     </td>
   *   </tr>
   *   <tr>
   *     <td>-v</td>
   *     <td>
   *       Verbose
   *     </td>
   *     <td>No, default is to be silent.</td>
   *   </tr>
   *   <tr>
   *     <td>{@code -out <path>}</td>
   *     <td>
   *       Path to file to write protocol's XML to.
   *     </td>
   *     <td>
   *       No.
   *     </td>
   *   </tr>
   * </table>
   * @param args Command line, see table above for accepted parameters.
   */
  public static void main(String[] args)
  {
    CommandLineParser clp=new CommandLineParser(args);
    
    try
    {
      boolean v=clp.exists("-v");
      
      String cname=clp.value("-class");
      
      File outf=clp.fileValue("-out");
      
      HttpProtocolDef p=null;
      
      if (cname!=null)
      {
        Class<?> clazz=Class.forName(cname);
      
        HttpProtocolFactory hpf=new HttpProtocolFactory();
        p=hpf.create(clazz);
      }
      else
      {
        File infp=clp.mandatoryFileValue("-in");
        p=HttpProtocolDef.parse(infp);
      }
      
      if (v) System.out.println(p);
      
      if (outf!=null)
      {
        String xml=p.toXml();
        IOUtil.save(outf, xml);
      }
      
      System.exit(0);
    }
    catch (Exception x)
    {
      x.printStackTrace();
      System.exit(-1);
    }
  }
  
}
